bitkeeper revision 1.1098.1.1 (4107c16f_jhuAGUiMBcotuiFhO6dHQ)
authorsos22@donkeykong.cl.cam.ac.uk <sos22@donkeykong.cl.cam.ac.uk>
Wed, 28 Jul 2004 15:08:31 +0000 (15:08 +0000)
committersos22@donkeykong.cl.cam.ac.uk <sos22@donkeykong.cl.cam.ac.uk>
Wed, 28 Jul 2004 15:08:31 +0000 (15:08 +0000)
Add a simple debugger for Xen and domain 0.  This isn't anything
like as complete as PDB; it can't access any userspace other than
the current process's and it can't make any changes to a running
process.  On the other hand, it's probably slightly less fragile.
The code's a lot cleaner, as well.

.rootkeys
BitKeeper/etc/logging_ok
xen/arch/x86/x86_32/xdb_trap.S [new file with mode: 0644]
xen/arch/x86/xdb.c [new file with mode: 0644]
xen/common/domain.c
xen/common/kernel.c
xen/common/keyhandler.c
xen/drivers/char/console.c
xen/drivers/char/serial.c

index b5b50680da93bbc5ffa18c31984e2e0a2043f316..43e1f1dfe44c3ccb4639e3f30adf1de27121f463 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3ddb79bcecupHj56ZbTa3B0FxDowMg xen/arch/x86/x86_32/entry.S
 3ddb79bcHwuCQDjBICDTSis52hWguw xen/arch/x86/x86_32/mm.c
 3ddb79bc4nTpGQOe6_-MbyZzkhlhFQ xen/arch/x86/x86_32/usercopy.c
+4107c15e_NqNYew2EXroXz2mgTAMWQ xen/arch/x86/x86_32/xdb_trap.S
 3ddb79bcOMCu9-5mKpjIh5d0qqBDPg xen/arch/x86/x86_32/xen.lds
 40e96d3aLDI-nViMuYneD7VKYlZrVg xen/arch/x86/x86_64/entry.S
 40e96d3ahBTZqbTViInnq0lM03vs7A xen/arch/x86/x86_64/usercopy.c
 40e96d3akN3Hu_J5Bk-WXD8OGscrYQ xen/arch/x86/x86_64/xen.lds
+4107c15e-VmEcLsE-7JCXZaabI8C7A xen/arch/x86/xdb.c
 3ddb79bdff-gj-jFGKjOejeHLqL8Lg xen/common/Makefile
 3e397e66AyyD5fYraAySWuwi9uqSXg xen/common/ac_timer.c
 4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c
index 34d574e882be97f5e5092ef5b30f13617d8f4dd4..36759e9962afe01d95abc666b9b5f67f27d3688b 100644 (file)
@@ -38,6 +38,7 @@ smh22@boulderdash.cl.cam.ac.uk
 smh22@labyrinth.cl.cam.ac.uk
 smh22@tempest.cl.cam.ac.uk
 smh22@uridium.cl.cam.ac.uk
+sos22@donkeykong.cl.cam.ac.uk
 sos22@labyrinth.cl.cam.ac.uk
 tlh20@elite.cl.cam.ac.uk
 tlh20@labyrinth.cl.cam.ac.uk
diff --git a/xen/arch/x86/x86_32/xdb_trap.S b/xen/arch/x86/x86_32/xdb_trap.S
new file mode 100644 (file)
index 0000000..262c6db
--- /dev/null
@@ -0,0 +1,35 @@
+.global trap_to_xendbg
+.extern __trap_to_xendbg
+       
+#define SAVE_ALL_NOSEGREGS \
+       pushw $0;  \
+        pushw %gs; \
+       pushw $0;  \
+        pushw %fs; \
+       pushw $0;  \
+        pushw %es; \
+       pushw $0;  \
+        pushw %ds; \
+        pushl %eax; \
+        pushl %ebp; \
+        pushl %edi; \
+        pushl %esi; \
+        pushl %edx; \
+        pushl %ecx; \
+        pushl %ebx;
+
+       // Save the register state and call __trap_to_xendbg 
+trap_to_xendbg:
+       pushw $0
+       pushw %ss
+       pushl %esp //We'll fix this up later, in __trap_to_xendbg, by adding 8
+       pushf
+       pushw $0
+       pushw %cs
+       pushl 16(%esp)
+1:     pushl $0                // Orig_eax
+       SAVE_ALL_NOSEGREGS
+       pushl %esp
+       call __trap_to_xendbg
+       add $72, %esp
+       ret
diff --git a/xen/arch/x86/xdb.c b/xen/arch/x86/xdb.c
new file mode 100644 (file)
index 0000000..8548953
--- /dev/null
@@ -0,0 +1,378 @@
+/* Simple hacked-up version of pdb for us in post-mortem debugging of
+   Xen and domain 0. This should be a little cleaner, hopefully.  Note
+   that we can't share a serial line with PDB. */
+#include <xen/lib.h>
+#include <asm/uaccess.h>
+#include <xen/serial.h>
+#include <asm/irq.h>
+#include <xen/spinlock.h>
+
+/* Printk isn't particularly safe just after we've trapped to the
+   debugger. so avoid it. */
+#define dbg_printk(...)
+
+static int
+xendbg_serhnd = -1;
+
+static void
+xendbg_put_char(u8 data)
+{
+       serial_putc(xendbg_serhnd, data);
+}
+
+static u8
+xendbg_get_char(void)
+{
+       u8 ch;
+       extern unsigned char __serial_getc(int handle);
+       ch = __serial_getc(xendbg_serhnd);
+       return ch;
+}
+
+static int
+hex_char_val(unsigned char c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       else if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       else if (c >= 'A' && c <= 'F')
+               return c - 'A' + 10;
+       else
+               BUG();
+}
+
+static unsigned char
+val_to_hex_char(unsigned val)
+{
+       if (val < 10)
+               return val + '0';
+       else if (val < 16)
+               return val - 10 + 'a';
+       else
+               BUG();
+}
+
+/* Receive a command.  Returns -1 on csum error, 0 otherwise. */
+/* Does not acknowledge. */
+static int
+attempt_receive_packet(char *recv_buf)
+{
+       int count;
+       u8 csum;
+       u8 received_csum;
+       u8 ch;
+
+       /* Skip over everything up to the first '$' */
+       while ((ch = xendbg_get_char()) != '$')
+               ;
+       csum = 0;
+       for (count = 0; count < 4096; count++) {
+               ch = xendbg_get_char();
+               if (ch == '#')
+                       break;
+               recv_buf[count] = ch;
+               csum += ch;
+       }
+       if (count == 4096) {
+               dbg_printk("WARNING: GDB sent a stupidly big packet.\n");
+               return -1;
+       }
+       recv_buf[count] = 0;
+       received_csum = hex_char_val(xendbg_get_char()) * 16 +
+               hex_char_val(xendbg_get_char());
+       if (received_csum == csum) {
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
+/* Send a string of bytes to the debugger. */
+static void
+xendbg_send(const char *buf, int count)
+{
+       int x;
+       for (x = 0; x < count; x++)
+               xendbg_put_char(buf[x]);
+}
+
+/* Receive a command, discarding up to ten packets with csum
+ * errors.  Acknowledges all received packets. */
+static int
+receive_command(char *recv_buf)
+{
+       int r;
+       int count;
+
+       count = 0;
+       do {
+               r = attempt_receive_packet(recv_buf);
+               if (r < 0)
+                       xendbg_send("-", 1);
+               else
+                       xendbg_send("+", 1);
+               count++;
+       } while (r < 0 && count < 10);
+       return r;
+}
+
+static void
+u32_to_hex_u8(unsigned char val, char *buf)
+{
+       if (val >= 256)
+               BUG();
+       sprintf(buf, "%.02x\n", val);
+}
+
+static void
+u32_to_hex_u32(unsigned val, char *buf)
+{
+       sprintf(buf, "%.08x\n", val);
+}
+
+static void
+xendbg_send_hex_u8(unsigned char val)
+{
+       char buf[2];
+       u32_to_hex_u8(val, buf);
+       xendbg_send(buf, 2);
+}
+
+static u8
+xendbg_reply_csum;
+
+static void
+xendbg_start_reply(void)
+{
+       xendbg_reply_csum = 0;
+       xendbg_send("$", 1);
+}
+
+static void
+xendbg_sendrep_data(const unsigned char *data, unsigned long len)
+{
+       int x;
+
+       for (x = 0; x < len; x++) {
+               xendbg_put_char(data[x]);
+               xendbg_reply_csum += data[x];
+       }
+}
+
+/* Return 0 if the reply was successfully received, !0 otherwise. */
+static int
+xendbg_finish_reply(void)
+{
+       char ch;
+
+       xendbg_send("#", 1);
+       xendbg_send_hex_u8(xendbg_reply_csum);
+       ch = xendbg_get_char();
+       if (ch == '+')
+               return 0;
+       else
+               return 1;
+}
+
+static void
+xendbg_sendrep_hex_u8(unsigned val)
+{
+       char buf[2];
+       u32_to_hex_u8(val, buf);
+       xendbg_sendrep_data(buf, 2);
+}
+
+static void
+xendbg_sendrep_hex_u32(unsigned val)
+{
+       char buf[8];
+       u32_to_hex_u32(val, buf);
+       xendbg_sendrep_data(buf, 8);
+}
+
+static void
+xendbg_sendrep_hex_u32_le(unsigned val)
+{
+       val = (((val >> 0) & 0xff) << 24) |
+               (((val >> 8) & 0xff) << 16) |
+               (((val >> 16) & 0xff) << 8) |
+               (((val >> 24) & 0xff) << 0);
+       xendbg_sendrep_hex_u32(val);
+}
+
+static int
+handle_memory_read_command(unsigned long addr, unsigned long length)
+{
+       int x;
+       unsigned char val;
+       int r;
+       unsigned old_s_limit;
+
+       dbg_printk("Memory read starting at %lx, length %lx.\n", addr,
+              length);
+       old_s_limit = current->addr_limit.seg;
+       current->addr_limit.seg = ~0;
+       xendbg_start_reply();
+       for (x = 0; x < length; x++) {
+               r = copy_from_user(&val, (void *)(addr + x), 1);
+               if (r != 0) {
+                       dbg_printk("Error reading from %lx.\n", addr + x);
+                       break;
+               }
+               xendbg_sendrep_hex_u8(val);
+       }
+       if (x == 0)
+               xendbg_sendrep_data("E05", 3);
+       dbg_printk("Read done.\n");
+       current->addr_limit.seg = old_s_limit;
+       return xendbg_finish_reply();
+}
+
+static int
+xendbg_send_reply(const char *buf)
+{
+       xendbg_start_reply();
+       xendbg_sendrep_data(buf, strlen(buf));
+       return xendbg_finish_reply();
+}
+
+static int
+handle_register_read_command(struct pt_regs *regs)
+{
+       xendbg_start_reply();
+       xendbg_sendrep_hex_u32_le(regs->eax);
+       xendbg_sendrep_hex_u32_le(regs->ecx);
+       xendbg_sendrep_hex_u32_le(regs->edx);
+       xendbg_sendrep_hex_u32_le(regs->ebx);
+       xendbg_sendrep_hex_u32_le(regs->esp);
+       xendbg_sendrep_hex_u32_le(regs->ebp);
+       xendbg_sendrep_hex_u32_le(regs->esi);
+       xendbg_sendrep_hex_u32_le(regs->edi);
+       xendbg_sendrep_hex_u32_le(regs->eip);
+       xendbg_sendrep_hex_u32_le(regs->eflags);
+       xendbg_sendrep_hex_u32_le(regs->xcs);
+       xendbg_sendrep_hex_u32_le(regs->xss);
+       xendbg_sendrep_hex_u32_le(regs->xes);
+       xendbg_sendrep_hex_u32_le(regs->xfs);
+       xendbg_sendrep_hex_u32_le(regs->xgs);
+       return xendbg_finish_reply();
+}
+
+static unsigned long
+hex_to_int(const char *start, const char **end)
+{
+       return simple_strtol(start, (char **)end, 16);
+}
+
+static int
+process_command(const char *received_packet, struct pt_regs *regs)
+{
+       const char *ptr;
+       unsigned long addr, length;
+       int retry;
+       int counter;
+       int resume = 0;
+
+       /* Repeat until gdb acks the reply */
+       counter = 0;
+       do {
+               switch (received_packet[0]) {
+               case 'g': /* Read registers */
+                       retry = handle_register_read_command(regs);
+                       break;
+               case 'm': /* Read memory */
+                       addr = hex_to_int(received_packet + 1, &ptr);
+                       if (ptr == received_packet + 1 ||
+                           ptr[0] != ',') {
+                               xendbg_send_reply("E03");
+                               return 0;
+                       }
+                       length = hex_to_int(ptr + 1, &ptr);
+                       if (ptr[0] != 0) {
+                               xendbg_send_reply("E04");
+                               return 0;
+                       }
+                       retry =
+                               handle_memory_read_command(addr,
+                                                          length);
+                       break;
+               case 'G': /* Write registers */
+               case 'M': /* Write memory */
+                       retry = xendbg_send_reply("E02");
+                       break;
+               case 'D':
+                       resume = 1;
+                       retry = xendbg_send_reply("");
+                       break;
+               case 'c': /* Resume at current address */
+               case 's': /* Single step */
+               case '?':
+                       retry = xendbg_send_reply("S01");
+                       break;
+               default:
+                       retry = xendbg_send_reply("");
+                       break;
+               }
+               counter++;
+       } while (retry == 1 && counter < 10);
+       if (retry) {
+               dbg_printk("WARNING: gdb disappeared when we were trying to send it a reply.\n");
+               return 1;
+       }
+       return resume;
+}
+
+void
+__trap_to_xendbg(struct pt_regs *regs)
+{
+       int resume = 0;
+       int r;
+       static int xendbg_running;
+       static char recv_buf[4096];
+
+       if (xendbg_serhnd < 0) {
+               dbg_printk("Debugger not ready yet.\n");
+               return;
+       }
+       /* We rely on our caller to ensure we're only on one processor
+        * at a time... We should probably panic here, but given that
+        * we're a debugger we should probably be a little tolerant of
+        * things going wrong. */
+       if (xendbg_running) {
+               dbg_printk("WARNING WARNING WARNING: Avoiding recursive xendbg.\n");
+               return;
+       }
+       xendbg_running = 1;
+       /* trap_to_xendbg gets esp slightly wrong.  Correct for this. */
+       regs->esp += 8;
+
+       dbg_printk("Waiting for GDB to attach to XenDBG\n");
+       /* Urgg... hope this is right... */
+       while (resume == 0) {
+               r = receive_command(recv_buf);
+               if (r < 0) {
+                       dbg_printk("GDB disappeared, trying to resume Xen...\n");
+                       resume = 1;
+               } else
+                       resume = process_command(recv_buf, regs);
+       }
+       xendbg_running = 0;
+}
+
+void
+initialize_xendbg(void)
+{
+       extern char opt_xendbg[];
+
+       if (!strcmp(opt_xendbg, "none"))
+               return;
+       xendbg_serhnd = parse_serial_handle(opt_xendbg);
+       if (xendbg_serhnd == -1)
+               panic("Can't parse %s as XDB serial info.\n", opt_xendbg);
+
+       /* Acknowledge any spurious GDB packets. */
+       xendbg_put_char('+');
+
+       printk("Xendbg initialised.\n");
+}
index 7082f07bed3de75dfb82d96ab5523d5d451336ff..68c7c79914e4dd7cc56d32e93ce8d73314ba7d89 100644 (file)
@@ -163,6 +163,8 @@ void domain_crash(void)
     BUG();
 }
 
+extern void trap_to_xendbg(void);
+
 void domain_shutdown(u8 reason)
 {
     struct domain *d;
@@ -172,6 +174,8 @@ void domain_shutdown(u8 reason)
         extern void machine_restart(char *);
         extern void machine_halt(void);
 
+       trap_to_xendbg();
+
         if ( reason == 0 ) 
         {
             printk("Domain 0 halted: Our work here is done.\n");
index 7facb69cacab95dfda1fd01ca114a570b8ee46ce..0864c9fab7868e3e6a18ff1eee86b07cd770f9d0 100644 (file)
@@ -64,6 +64,8 @@ int opt_ignorebiostables=0;
 int opt_watchdog=0;
 /* opt_pdb: Name of serial port for Xen pervasive debugger (and enable pdb) */
 unsigned char opt_pdb[10] = "none";
+/* opt_pdb: Name of serial port for Xen debugger (and enable xendbg) */
+unsigned char opt_xendbg[10] = "none";
 /* opt_tbuf_size: trace buffer size (in pages) */
 unsigned int opt_tbuf_size = 10;
 /* opt_sched: scheduler - default to Borrowed Virtual Time */
@@ -98,6 +100,7 @@ static struct {
     { "ignorebiostables",  OPT_BOOL, &opt_ignorebiostables },
     { "watchdog",          OPT_BOOL, &opt_watchdog },
     { "pdb",               OPT_STR,  &opt_pdb },
+    { "xendbg",            OPT_STR,  &opt_xendbg },
     { "tbuf_size",         OPT_UINT, &opt_tbuf_size },
     { "sched",             OPT_STR,  &opt_sched },
     { "physdev_dom0_hide", OPT_STR,  &opt_physdev_dom0_hide },
@@ -108,6 +111,9 @@ static struct {
 };
 
 
+void initialize_xendbg(void);
+void trap_to_xendbg(void);
+
 void cmain(multiboot_info_t *mbi)
 {
     struct domain *new_dom;
@@ -163,6 +169,8 @@ void cmain(multiboot_info_t *mbi)
 
     init_console();
 
+    initialize_xendbg();
+
     /* HELLO WORLD --- start-of-day banner text. */
     printk(XEN_BANNER);
     printk(" http://www.cl.cam.ac.uk/netos/xen\n");
@@ -291,6 +299,10 @@ void cmain(multiboot_info_t *mbi)
 
     shadow_mode_init();
 
+    printk("Trapping to debugger.\n");
+    trap_to_xendbg();
+    printk("Trapped to debugger and came back.\n");
+
     /*
      * We're going to setup domain0 using the module(s) that we stashed safely
      * above our heap. The second module, if present, is an initrd ramdisk.
index aff7ccfd1b62851dd26f0c0ec634df9f10748aeb..ad04f8476dc51923a7d76b53dc3e037630c2beea 100644 (file)
@@ -115,6 +115,11 @@ extern void perfc_reset(unsigned char key, void *dev_id,
                         struct pt_regs *regs);
 #endif
 
+void do_panic_key(unsigned char key, void *dev_id, struct pt_regs *regs)
+{
+    panic("Panic requested from console");
+}
+
 void initialize_keytable(void)
 {
     add_key_handler('d', dump_registers, "dump registers"); 
@@ -128,4 +133,5 @@ void initialize_keytable(void)
     add_key_handler('p', perfc_printall, "print performance counters"); 
     add_key_handler('P', perfc_reset,    "reset performance counters"); 
 #endif
+    add_key_handler('%', do_panic_key,   "Panic Xen");
 }
index b1887fa1fbb4585d143ba5e245c2748c006b80cf..7008bef76e8d88e1e55d3753b81b9fb18a1c8cad 100644 (file)
@@ -440,6 +440,8 @@ void console_endboot(int disable_vga)
  * **************************************************************
  */
 
+extern void trap_to_xendbg(void);
+
 void panic(const char *fmt, ...)
 {
     va_list args;
@@ -450,7 +452,9 @@ void panic(const char *fmt, ...)
     va_start(args, fmt);
     (void)vsnprintf(buf, sizeof(buf), fmt, args);
     va_end(args);
-    
+
+    trap_to_xendbg();
+
     /* Spit out multiline message in one go. */
     spin_lock_irqsave(&console_lock, flags);
     __putstr("\n****************************************\n");
index e6ae6c60b9f282f1c320403093b8492446a8f3f5..7c710cbada3177ef8ca2784522bd03439c421f39 100644 (file)
@@ -420,29 +420,17 @@ static int byte_matches(int handle, unsigned char *pc)
     return 0;
 }
 
-unsigned char serial_getc(int handle)
+unsigned char __serial_getc(int handle)
 {
     uart_t *uart = &com[handle & SERHND_IDX];
     unsigned char c;
-    unsigned long flags;
 
-    spin_lock_irqsave(&uart->lock, flags);
-
-    while ( uart->rxbufp != uart->rxbufc )
-    {
-        c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
-        if ( byte_matches(handle, &c) )
-            goto out;
-    }
-    
-    disable_irq(uart->irq);
-    
     /* disable_irq() may have raced execution of uart_rx(). */
     while ( uart->rxbufp != uart->rxbufc )
     {
         c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
         if ( byte_matches(handle, &c) )
-            goto enable_and_out;
+         return c;
     }
 
     /* We now wait for the UART to receive a suitable character. */
@@ -452,8 +440,29 @@ unsigned char serial_getc(int handle)
         c = inb(uart->io_base + RBR);
     }
     while ( !byte_matches(handle, &c) );
-    
- enable_and_out:
+
+    return c;
+}
+
+unsigned char serial_getc(int handle)
+{
+    uart_t *uart = &com[handle & SERHND_IDX];
+    unsigned char c;
+    unsigned long flags;
+
+    spin_lock_irqsave(&uart->lock, flags);
+
+    while ( uart->rxbufp != uart->rxbufc )
+    {
+        c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
+        if ( byte_matches(handle, &c) )
+            goto out;
+    }
+
+    disable_irq(uart->irq);
+
+    c = __serial_getc(handle);
+
     enable_irq(uart->irq);
  out:
     spin_unlock_irqrestore(&uart->lock, flags);